using System;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System.Threading;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.cache
{


	public class AdaptiveCacheManager
	{
		private static readonly Logger log = Logger.getLogger(typeof(AdaptiveCacheManager).Name);

		private float decreaseRatio = 1.15f;
		private float increaseRatio = 1.1f;

		private readonly List<AdaptiveCacheElement> caches = new LinkedList<AdaptiveCacheElement>();

//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: private final List<SoftLruCache<?,?>> softCaches = new ArrayList<SoftLruCache<?,?>>();
		private readonly List<SoftLruCache<?, ?>> softCaches = new List<SoftLruCache<?, ?>>();

		private AdaptiveCacheWorker workerThread;

		[MethodImpl(MethodImplOptions.Synchronized)]
		public virtual void registerCache<T1>(Cache<T1> cache, float ratio, int minSize)
		{
			if (cache == null || ratio >= 1 || ratio <= 0 || minSize < 0)
			{
				throw new IllegalArgumentException(" cache=" + cache + " ratio =" + ratio + " minSize=" + minSize);
			}

//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: if (cache instanceof SoftLruCache<?,?>)
			if (cache is SoftLruCache<?, ?>)
			{
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: softCaches.add((SoftLruCache<?,?>) cache);
				softCaches.Add((SoftLruCache<?, ?>) cache);
				return;
			}

			foreach (AdaptiveCacheElement element in caches)
			{
				if (element.getCache() == cache)
				{
					log.fine("Cache[" + cache.Name + "] already registered.");
					return;
				}

			}
			AdaptiveCacheElement element = new AdaptiveCacheElement(cache, ratio, minSize);
			caches.Add(element);
			cache.setAdaptiveStatus(true);
			log.fine("Cache[" + cache.Name + "] threshold=" + ratio + "minSize=" + minSize + " registered.");
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		public virtual void unregisterCache<T1>(Cache<T1> cache)
		{
			if (cache == null)
			{
				throw new IllegalArgumentException("Null cache");
			}
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: if (cache instanceof SoftLruCache<?,?>)
			if (cache is SoftLruCache<?, ?>)
			{
				softCaches.Remove(cache);
				return;
			}
			List<AdaptiveCacheElement>.Enumerator itr = caches.GetEnumerator();
			while (itr.MoveNext())
			{
				AdaptiveCacheElement element = itr.Current;
				if (element.getCache() == cache)
				{
					itr.Remove();
					break;
				}
			}
			log.fine("Cache[" + cache.Name + "] removed.");
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		internal virtual AdaptiveCacheElement getAdaptiveCacheElementIndex<T1>(Cache<T1> cache)
		{
			foreach (AdaptiveCacheElement element in caches)
			{
				if (element.getCache() == cache)
				{
					return element;
				}
			}
			return null;
		}

		private void parseParams(Map<object, object> @params)
		{
			if (@params == null)
			{
				return;
			}
			if (@params.containsKey("adaptive_cache_worker_sleep_time"))
			{
				object @value = @params.get("adaptive_cache_worker_sleep_time");
				int sleepTime = 3000;
				try
				{
					sleepTime = Convert.ToInt32((string) @value);
				}
				catch (NumberFormatException e)
				{
					log.warning("Unable to parse apdaptive_cache_worker_sleep_time " + @value);
				}
				workerThread.setSleepTime(sleepTime);
			}
			if (@params.containsKey("adaptive_cache_manager_decrease_ratio"))
			{
				object @value = @params.get("adaptive_cache_manager_decrease_ratio");
				try
				{
					decreaseRatio = Convert.ToSingle((string) @value);
				}
				catch (NumberFormatException e)
				{
					log.warning("Unable to parse adaptive_cache_manager_decrease_ratio " + @value);
				}
				if (decreaseRatio < 1)
				{
					decreaseRatio = 1.0f;
				}
			}
			if (@params.containsKey("adaptive_cache_manager_increase_ratio"))
			{
				object @value = @params.get("adaptive_cache_manager_increase_ratio");
				try
				{
					increaseRatio = Convert.ToSingle((string) @value);
				}
				catch (NumberFormatException e)
				{
					log.warning("Unable to parse adaptive_cache_manager_increase_ratio " + @value);
				}
				if (increaseRatio < 1)
				{
					increaseRatio = 1.0f;
				}
			}
		}

		public virtual void start(Map<object, object> @params)
		{
			workerThread = new AdaptiveCacheWorker();
			parseParams(@params);
			workerThread.Start();
		}

		public virtual void stop()
		{
			workerThread.markDone();
			workerThread = null;
		}

		internal virtual Collection<AdaptiveCacheElement> getCaches()
		{
			return caches;
		}

		private class AdaptiveCacheWorker : Thread
		{
			private bool done = false;
			private int sleepTime = 3000;

			internal AdaptiveCacheWorker() : base("AdaptiveCacheWorker")
			{

			}

			internal virtual void setSleepTime(int sleepTime)
			{
				this.sleepTime = sleepTime;
			}

			[MethodImpl(MethodImplOptions.Synchronized)]
			public virtual void run()
			{
				while (!done)
				{
					try
					{
						adaptCaches();
						adaptSoftCaches();
						this.wait(sleepTime);
					}
					catch (InterruptedException e)
					{
						Thread.interrupted();
					}
				}
			}

			internal virtual void markDone()
			{
				done = true;
			}
		}

		public virtual void adaptCaches()
		{
			List<AdaptiveCacheElement> copy = new LinkedList<AdaptiveCacheElement>();
			lock (this)
			{
				copy.AddRange(caches);
			}
			foreach (AdaptiveCacheElement element in copy)
			{
				adaptCache(element);
			}
		}

		[MethodImpl(MethodImplOptions.Synchronized)]
		public virtual void adaptSoftCaches()
		{
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: for (SoftLruCache<?,?> cache : softCaches)
			foreach (SoftLruCache<?, ?> cache in softCaches)
			{
				cache.pollAll();
			}
		}

		public virtual void adaptCache<T1>(Cache<T1> cache)
		{
			if (cache == null)
			{
				throw new IllegalArgumentException("Null cache");
			}

			AdaptiveCacheElement element = getAdaptiveCacheElementIndex(cache);
			if (element != null)
			{
				adaptCache(element);
			}
		}


		private void adaptCache(AdaptiveCacheElement element)
		{
			long max = Runtime.getRuntime().maxMemory();
			long total = Runtime.getRuntime().totalMemory();
			long free = Runtime.getRuntime().freeMemory();

			float ratio = (float)(max - free) / max;
			float allocationRatio = (float) total / max;
			if (allocationRatio < element.getRatio())
			{
			// allocation ratio < 1 means JVM can still increase heap size
			// we won't decrease caches until element ratio is less
			// then allocationRatio
				ratio = 0;
			}
		// System.out.println( "max= " + max + " total=" + total +
		// " ratio= " + ratio + " ... free=" + free );
			if (ratio > element.getRatio())
			{
			// decrease cache size
			// after decrease we resize again with +1000 to avoid
			// spam of this method
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: Cache<?,?> cache = element.getCache();
				Cache<?, ?> cache = element.getCache();
				int newCacheSize = (int)(cache.maxSize() / decreaseRatio / (1 + (ratio - element.getRatio())));
				int minSize = element.minSize();
				if (newCacheSize < minSize)
				{
					log.fine("Cache[" + cache.Name + "] cannot decrease under " + minSize + " (allocation ratio=" + allocationRatio + " threshold status=" + ratio + ")");
					cache.resize(minSize);
					cache.resize(minSize + 1000);
					return;
				}
				if (newCacheSize + 1200 > cache.size())
				{
					if (cache.size() - 1200 >= minSize)
					{
						newCacheSize = cache.size() - 1200;
					}
					else
					{
						newCacheSize = minSize;
					}
				}
				log.fine("Cache[" + cache.Name + "] decreasing from " + cache.size() + " to " + newCacheSize + " (allocation ratio=" + allocationRatio + " threshold status=" + ratio + ")");
				if (newCacheSize <= 1000)
				{
					cache.Clear();
				}
				else
				{
					cache.resize(newCacheSize);
				}
				cache.resize(newCacheSize + 1000);
			}
			else
			{
			// increase cache size
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: Cache<?,?> cache = element.getCache();
				Cache<?, ?> cache = element.getCache();
				if (cache.size() / (float) cache.maxSize() < 0.9f)
				{
					return;
				}
				int newCacheSize = (int)(cache.maxSize() * increaseRatio);
				log.fine("Cache[" + cache.Name + "] increasing from " + cache.size() + " to " + newCacheSize + " (allocation ratio=" + allocationRatio + " threshold status=" + ratio + ")");
				cache.resize(newCacheSize);
			}

		}

		private class AdaptiveCacheElement
		{
//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: private final Cache<?,?> cache;
			private readonly Cache<?, ?> cache;
			private readonly float ratio;
			private readonly int minSize;

			internal AdaptiveCacheElement<T1>(Cache<T1> cache, float ratio, int minSize)
			{
				this.cache = cache;
				this.ratio = ratio;
				this.minSize = minSize;
			}

			internal virtual string Name
			{
				get
				{
					return cache.Name;
				}
			}

//JAVA TO VB & C# CONVERTER TODO TASK: Java wildcard generics are not converted to .NET:
//ORIGINAL LINE: Cache<?,?> getCache()
			internal virtual Cache<?, ?> getCache()
			{
				return cache;
			}

			internal virtual float getRatio()
			{
				return ratio;
			}

			internal virtual int minSize()
			{
				return minSize;
			}
		}
	}
}